{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "a321c3df",
   "metadata": {},
   "source": [
    "# 原则二：单一职责原则（Single Responsibility Principle）\n",
    "\n",
    "## 定义\n",
    "A class should have a single responsibility, where a responsibility is nothing but a reason to change.\n",
    "\n",
    "即：一个类只允许有一个职责，即只有一个导致该类变更的原因。\n",
    "\n",
    "## 定义的解读\n",
    "- 类职责的变化往往就是导致类变化的原因：也就是说如果一个类具有多种职责，就会有多种导致这个类变化的原因，从而导致这个类的维护变得困难。\n",
    "\n",
    "- 往往在软件开发中随着需求的不断增加，可能会给原来的类添加一些本来不属于它的一些职责，从而违反了单一职责原则。如果我们发现当前类的职责不仅仅有一个，就应该将本来不属于该类真正的职责分离出去。\n",
    "\n",
    "- 不仅仅是类，函数（方法）也要遵循单一职责原则，即：一个函数（方法）只做一件事情。如果发现一个函数（方法）里面有不同的任务，则需要将不同的任务以另一个函数（方法）的形式分离出去。\n",
    "\n",
    "\n",
    "## 优点\n",
    "如果类与方法的职责划分得很清晰，不但可以提高代码的可读性，更实际性地更降低了程序出错的风险，因为清晰的代码会让bug无处藏身，也有利于bug的追踪，也就是降低了程序的维护成本。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "420cd5e6",
   "metadata": {},
   "source": [
    "## 代码讲解\n",
    "单一职责原则的demo比较简单，通过对象（属性）的设计上讲解已经足够，不需要具体的客户端调用。我们先看一下需求点：\n",
    "\n",
    "### 需求点\n",
    "\n",
    "初始需求：需要创造一个员工类，这个类有员工的一些基本信息。\n",
    "\n",
    "新需求：增加两个方法：\n",
    "\n",
    "判定员工在今年是否升职\n",
    "计算员工的薪水\n",
    "先来看一下不好的设计：\n",
    "\n",
    "### 不好的设计"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "5c4db158",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Employee(object):\n",
    "    def __init__(self, name, address, employee_id):\n",
    "        self.name = name  # 员工姓名\n",
    "        self.address = address  # 员工住址\n",
    "        self.employee_id = employee_id  # 员工ID\n",
    "        \n",
    "    def calculateSalary(self):\n",
    "        \"\"\" 计算薪水 \"\"\"\n",
    "        pass\n",
    "        \n",
    "    def willGetPromotionThisYear(self):\n",
    "        \"\"\" 今年是否晋升 \"\"\"\n",
    "        pass   "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a55ec286",
   "metadata": {},
   "source": [
    "由上面的代码可以看出：\n",
    "\n",
    "- 在初始需求下，我们创建了Employee这个员工类，并声明了3个员工信息的属性：员工姓名，地址，员工ID。\n",
    "- 在新需求下，两个方法直接加到了员工类里面。\n",
    "\n",
    "新需求的做法看似没有问题，因为都是和员工有关的，但却违反了单一职责原则：因为这两个方法并不是员工本身的职责。\n",
    "\n",
    "- calculateSalary这个方法的职责是属于会计部门的：薪水的计算是会计部门负责。\n",
    "- willPromotionThisYear这个方法的职责是属于人事部门的：考核与晋升机制是人事部门负责。\n",
    "\n",
    "而上面的设计将本来不属于员工自己的职责强加进了员工类里面，而这个类的设计初衷（原始职责）就是单纯地保留员工的一些信息而已。因此这么做就是给这个类引入了新的职责，故此设计违反了单一职责原则。\n",
    "\n",
    "我们可以简单想象一下这么做的后果是什么：如果员工的晋升机制变了，或者税收政策等影响员工工资的因素变了，我们还需要修改当前这个类。\n",
    "\n",
    "那么怎么做才能不违反单一职责原则呢？- 我们需要将这两个方法（责任）分离出去，让本应该处理这类任务的类来处理。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "681372e4",
   "metadata": {},
   "source": [
    "### 较好的设计\n",
    "\n",
    "我们保留员工类的基本信息："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "0f8ba712",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Employee(object):\n",
    "    def __init__(self, name, address, employee_id):\n",
    "        self.name = name  # 员工姓名\n",
    "        self.address = address  # 员工住址\n",
    "        self.employee_id = employee_id  # 员工ID\n",
    "        \n",
    "# 接着创建新的会计部门类：\n",
    "class FinancialApartment(object):\n",
    "    \"\"\" 会计部门类 \"\"\"\n",
    "    def calculateSalary(employee: Employee):\n",
    "        \"\"\" 计算薪水 \"\"\"\n",
    "        return 0.0\n",
    "    \n",
    "# 和人事部门类：\n",
    "class HRApartment(object):\n",
    "    \"\"\" 人事部门类 \"\"\"\n",
    "    def willGetPromotionThisYear(employee: Employee):\n",
    "        \"\"\" 今年是否晋升 \"\"\"\n",
    "        return True   "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "70b9a969",
   "metadata": {},
   "source": [
    "通过创建了两个分别专门处理薪水和晋升的部门，会计部门和人事部门的类：FinancialApartment 和 HRApartment，把两个任务（责任）分离了出去，让本该处理这些职责的类来处理这些职责。\n",
    "\n",
    "这样一来，不仅仅在此次新需求中满足了单一职责原则，以后如果还要增加人事部门和会计部门处理的任务，就可以直接在这两个类里面添加即可。\n",
    "\n",
    "下面来看一下这两个设计的UML 类图，可以更形象地看出两种设计上的区别：\n",
    "\n",
    "## UML 类图对比\n",
    "\n",
    "未实践单一职责原则：\n",
    "![101](pic/102-101.png)\n",
    "\n",
    "实践了单一职责原则：\n",
    "![101](pic/102-102.png)\n",
    "\n",
    "可以看到，在实践了单一职责原则的 UML 类图中，不属于Employee的两个职责被分类了FinancialApartment类 和 HRApartment类。（在 UML 类图中，虚线箭头表示依赖关系，常用在方法参数等，由依赖方指向被依赖方）\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aecd385d",
   "metadata": {},
   "source": [
    "上面说过除了类要遵循单一职责设计原则之外，在函数（方法）的设计上也要遵循单一职责的设计原则。因函数（方法）的单一职责原则理解起来比较容易，故在这里就不提供Demo和UML 类图了。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "25653a61",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "76b9bed4",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2532d4db",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cb96da09",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "99b68e09",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "397de2b8",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "53724443",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eacdc167",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "29712c47",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
